/*! \file container.cpp	
	
	This contains the implementation of the Container class
*/

#include <GL/glfw.h>
#include "container.h"
#include "guiclass.h"
#include "image.h"
Container::Container( Component* parent, bool border ) :
	Component( parent ),
	draggable( false ),
	resizable( false ),
	dragging( false ),
	resize( 0 ),
	minWidth( RESIZE_MIN_WIDTH ),
	minHeight( RESIZE_MIN_HEIGHT )
	{
		this->border = border;
		focus = 0;
		okayButton = 0;
		cancelButton = 0;
		//~ cout << "Border tex " << border << endl;
	}

bool Container::handleMouseWheel( int pos ) {
	if ( !visible ) {
		return false;
	}
	// Only the focused container can handle key input
	if ( this != GUI::getInstance()->getFocusedComponent() ) {
		return false;
	}
	
	// This appears in mouseClick too
	if ( focus == 0 ) {
		focus = *(children.begin());
		tabTraverse();
	}
	
	focus->handleMouseWheel( pos );
	return true;
	//~ focus->setBackgroundColor( Color(1.0f), Color(1.0f, 1.0f, 1.0f ) );
}
	
void Container::sendMouseUpEvent() {
	Component::sendMouseUpEvent();
	
	dragging = false;
	resize = 0;
	list<Component*>::iterator i;
	for ( i = children.begin(); i != children.end(); ++i ) {
		(*i)->sendMouseUpEvent();
	}
}

void Container::sendOnLeaveEvent() {
	Component::sendOnLeaveEvent();
	
	list<Component*>::iterator i;
	for ( i = children.begin(); i != children.end(); ++i ) {
		(*i)->sendOnLeaveEvent();
	}
	
}

bool Container::handleMouseMove( int mX, int mY ) { 
	if ( !visible ) {
		return false;
	}
	
	// mX and mY are in screen coordinates, which is what we want for dragging and resizing
	// (and propagating the mouse event)
	
	if ( dragging ) {
		x = mX - dragX;
		y = mY - dragY;
	}
	
	// relative to parent, NOT SCREEN DO NOT CHANGE THIS TO GETSCREENX
	int relMX = mX - parent->getX();
	int relMY = mY - parent->getY();
	if ( resize ) {
		if ( resize & RESIZE_LEFT ) {
			int newWidth = x + w - relMX;
			if ( newWidth > minWidth ) {
				setWidth( newWidth );
				x = relMX;
			}
		}
		if ( resize & RESIZE_RIGHT ) {
			int newWidth = relMX - x;
			if ( newWidth > minWidth ) {
				setWidth( newWidth );
			}
		}
		if ( resize & RESIZE_TOP ) {
			int newHeight = y + h - relMY;
			if ( newHeight > minHeight ) {
				setHeight( newHeight );
				y = relMY;
			}
		}
		if ( resize & RESIZE_BOTTOM ) {
			int newHeight = relMY - y;
			if ( newHeight > minHeight ) {
				setHeight( newHeight );
			}
		}
	}
	
	// You don't even need to be IN the component for the mouseMove event to work...
	// This is so that you can drag panels
	vector<Listener*>::iterator j;
	for ( j = listeners.begin(); j != listeners.end(); ++j ) {
		((MouseListener*)(*j))->mouseMove( Event( this ) );
	}
	
	// The mouse doesn't even have to be inside this component for a mouseMove event to work.
	// This is useful for Scrollbar, specifically the slider-bar. If you click and drag it, you don't
	// want it to only work from within the ScrollPanel;
	list<Component*>::iterator i;
	for ( i = children.begin(); i != children.end(); ++i ) {
		(*i)->handleMouseMove( mX, mY );
	}
	
	mX -= getScreenX();
	mY -= getScreenY();
	if ( mX >= 0 && mX <= w && mY >= 0 && mY <= h ) {	
		// Go through all of the listeners and trigger their onEnter events
		if ( mouseIsOver == false ) {
			mouseIsOver = true;
			for ( j = listeners.begin(); j != listeners.end(); ++j ) {
				((MouseListener*)(*j))->onEnter( Event( this ) );
			}
		}
		GUI::getInstance()->setLockedComponent( this );
		return true;
	} else { // mouse is not in container
		// If the mouse WAS just in the container, then that means it just left
		if ( mouseIsOver ) {
			// Update all of this container's listeners
			vector<Listener*>::iterator j;
			for ( j = listeners.begin(); j != listeners.end(); ++j ) {
				((MouseListener*)(*j))->onLeave( Event( this ) );
			}
			
			list<Component*>::iterator i;
			for ( i = children.begin(); i != children.end(); ++i ) {
				(*i)->handleMouseMove( mX, mY );
			}
			mouseIsOver = true;
		}
		mouseIsOver = false;
	}
	return false;
}

bool Container::handleMouseClick( int button, int state ) {
	// If this isn't a top-level container, then it can get the focus.
	if ( parent != GUI::getInstance() ) {
		Component::handleMouseClick( button, state );
	}
	
	if ( !visible ) {
		return false;
	}
	int mX, mY;
	glfwGetMousePos( &mX, &mY );
	// Make the coordinates relative to the upper-left of this component
	mX -= getScreenX();
	mY -= getScreenY();
	
	// Handle children's click events first. This will come in handy for when there is a drop-down menu
	// in front of a button. You'd want to click the menu item, but not the button beneath it.
	bool handled = false;
	list<Component*>::iterator i;
	for ( i = children.begin(); i != children.end(); ++i ) {
		if ( (*i)->handleMouseClick( button, state ) ) {
			// If something inside this container handled the click, then that means
			// it was given the focus, so we need to give this container the focus and lock as well
			GUI::getInstance()->setFocusedComponent( this );
			GUI::getInstance()->setLockedComponent( this );
			handled = true;
			break; // only one thing can handle the click
		}
	}
	
	if ( !handled ) {
		if ( state == GLFW_RELEASE ) {
			mouseIsDown = false;
			dragging = false;
			resize = 0;
			// If this was the lock, then remove the lock on it
			if ( this == GUI::getInstance()->getLockedComponent() ) {
				GUI::getInstance()->setLockedComponent( 0 );
			}
			vector<Listener*>::iterator j;
			for ( j = listeners.begin(); j != listeners.end(); ++j ) {
				(*j)->mouseUp( Event( this ) );
			}
		}
		if ( mX >= 0 && mX <= w && mY >= 0 && mY <= h ) {
			if ( state == GLFW_PRESS ) {
				GUI::getInstance()->setFocusedComponent( this );
				GUI::getInstance()->setLockedComponent( this );
				if ( focus == 0 ) {
					// If you never set the focus before this, then set it now
					focus = *(children.begin());
					tabTraverse(); // make sure it is a focusable component
				}
				mouseIsDown = true;
				handled = true;
				if ( resizable ) {
						if ( mY < borderWidth ) {
							resize |= RESIZE_TOP;
						}
						if ( mY > h - RESIZE_BORDER && mY <= h ) {
							resize |= RESIZE_BOTTOM;
						}
						if ( mX < RESIZE_BORDER ) {
							resize |= RESIZE_LEFT;
						}
						if ( mX > w - RESIZE_BORDER && mX <= w ) {
							resize |= RESIZE_RIGHT;
						}
						//~ if ( resize ) {
							//~ GUI::getInstance()->setLockedComponent( this );
						//~ }
				}
				// If you just resized, then you probably don't want to drag
				if ( !resize && draggable && mY <= DRAG_HEIGHT ) {
					dragX = mX;
					dragY = mY;
					dragging = true;
					//~ GUI::getInstance()->setLockedComponent( this );
				} else {
					vector<Listener*>::iterator j;
					for ( j = listeners.begin(); j != listeners.end(); ++j ) {
						((MouseListener*)(*j))->mouseDown( Event( this ) );
					}
				}
			}
		}
	}
	
	return handled;
	
}

void Container::childChangedSize( Component* which ) {
	//~ cout << "One of this container's children changed size." << endl;	
}

void Container::tabTraverse() {
	// If there are no children, then there's nothing to tab over
	if ( children.empty() ) {
		return;
	}
	// First, we set a counter to 10 (counts down to 0) so that we don't
	// tab through more than 10 things automatically. Why is this here?
	// Well, if you make a panel with just StaticText, which isn't focusable,
	// then it'll keep looping through to try to find something that's focusable,
	// but that doesn't exist in such a scenario.
	if ( !GUI::getInstance()->shiftHeld() ) {
		int focusNotFound = 10;
		list<Component*>::iterator i;
		while ( focusNotFound ) {
			// Go through all of the children components
			for ( i = children.begin(); i != children.end(); ++i ) {
				// Find the current thing with the focus
				if ( (*i) == focus ) {
					// Check the component after this one in the list
					++i;
					// If the focus is the last component, then we set the focus to be the first component
					if ( i == children.end() ) {
						focus = *(children.begin());
						break;
					} else {
						focus = *i;
						break;
					}
				}
			}
			// Make sure this new focus can actually get the focus
			if ( focus->canHaveFocus() && focus->isVisible() ) {
				focusNotFound = 0;
			} else {
				focusNotFound--;
			}
		}
	} else {
		// Same code as above but with shift held, so go backwards tab order
		// (just use reverse iterators with the exact same code)
		int focusNotFound = 10;
		list<Component*>::reverse_iterator i;
		while ( focusNotFound ) {
			for ( i = children.rbegin(); i != children.rend(); ++i ) {
				if ( (*i) == focus ) {
					++i;
					if ( i == children.rend() ) {
						focus = *(children.rbegin());
						break;
					} else {
						focus = *i;
						break;
					}
				}
			}
			if ( focus->canHaveFocus() && focus->isVisible() ) {
				focusNotFound = 0;
			} else {
				focusNotFound--;
			}
		}
	}
}

void Container::handleKeyInput( int key, int state ) {
	// Can't see it ==> can't type in it
	if ( !visible ) {
		return;
	}
	
	// Only the focused container can handle key input
	if ( this != GUI::getInstance()->getFocusedComponent() ) {
		return;
	}
	
	// This appears in mouseClick too
	if ( focus == 0 ) {
		focus = *(children.begin());
		tabTraverse();
	}
	
	if ( state == GLFW_PRESS ) {
		if ( key == GLFW_KEY_TAB ) { // kitab = book
			tabTraverse();
		} else if ( key == GLFW_KEY_ENTER ) {
			// Pressing enter on this component will really press enter on the
			// "okayButton" if one is set. This makes it easy to type in a username
			// and password and just press enter.
			if ( okayButton != 0 ) {
				okayButton->handleKeyInput( GLFW_KEY_ENTER, state );
				return;
			}
		} else if ( key == GLFW_KEY_ESC ) {
			// Pressing escape on this component will really press enter on the
			// "cancelButton" if one is set. That way, you can close a window or something
			// easier.
			if ( cancelButton != 0 ) {
				cancelButton->handleKeyInput( GLFW_KEY_ENTER, state );
				return;
			}
		}
	}
	
	focus->handleKeyInput( key, state );
	
	
	//~ if ( !handled ) {
		//~ if ( state == GLFW_PRESS ) {
			//~ handled = true;
			//~ list<Listener*>::iterator j;
			//~ for ( j = listeners.begin(); j != listeners.end(); ++j ) {
				//~ if ( (*j)->getType() == TYPE_KEYLIST ) {
					//~ ((KeyListener*)(*j))->keyDown( Event( this, key ) );
				//~ }
			//~ }
		//~ }
		//~ if ( state == GLFW_RELEASE ) {
			//~ list<Listener*>::iterator j;
			//~ for ( j = listeners.begin(); j != listeners.end(); ++j ) {
				//~ if ( (*j)->getType() == TYPE_KEYLIST ) {
					//~ ((KeyListener*)(*j))->keyUp( Event( this, key ) );
				//~ }
			//~ }
		//~ }
	//~ }
	
	//~ return handled;
	
}

	
void Container::add( Component* c ) {
	//~ cout << "Adding a component to a container " << endl;
	children.push_back( c );
}

void Container::draw() {
	if ( !visible ) {
		return;
	}
	
	//~ glLoadIdentity();
	
	int screenX = getScreenX(), screenY = getScreenY();
	//~ glTranslatef( (float)screenX, (float)screenY, 0.0 );
	
	
	// PROBLEM:
	// drawRectangle and guiBorder are both based on screen coordinates or at least the translate coordinates
	
	//~ GUI::getInstance()->drawRectangle( screenX, screenY, w, h, bgColorTop, bgColorBottom );
	//~ GUI::getInstance()->drawRectangle( x, y, w, h, bgColorTop, bgColorBottom );
	GUI::getInstance()->drawRectangle( w, h, bgColorTop, bgColorBottom );
	// Iterate through its children and draw them. The parent positions
	// the start location of the child
	if ( !children.empty() ) {
		list<Component*>::iterator i;
		
		for ( i = children.begin(); i != children.end(); ++i ) {
			if ( (*i)->isVisible() ) {
				glPushMatrix();
				glTranslatef( (float)(*i)->getX(), (float)(*i)->getY(), 0.0f );
				(*i)->draw();
				glPopMatrix();
			}
		}
	}
	if ( border != false ) {
		//~ ImageHandler::getInstance()->guiBorder( sideID1, cornerID1, screenX, screenY, w, h );
		ImageHandler::getInstance()->guiBorder( sideID1, cornerID1, w, h );
	}
}

void Container::update() {
	if ( !visible ) {
		return;
	}
	
	if ( !children.empty() ) {
		list<Component*>::iterator i;
		
		for ( i = children.begin(); i != children.end(); ++i ) {
			if ( (*i)->isVisible() ) {
				(*i)->update();
			}
		}
	}
}

void Container::layout() {
	//~ cout << "Default Container::layout()" << endl;
}

void Container::setSize( int newW, int newH ) {
	Component::setSize( newW, newH );
	layout();
}
